﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using underwater.Processes;
using System.Xml;
using Parsing;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace underwater.OS
{
    class Interpritator
    {
        public enum ApiCommands { None, Move, DeleteMe, CreateNew, GiveMeListProcess, DeleteNumber } // Команды интерпритатора
        public enum ApiErrors { None, EndOfRegion, EndOfWater, Collision, MustDie }// Ошибки коллизий
        // 0    1              2             3         4


        

        public Interpritator()
        {

        }

        public void InitProc(Process proc)
        {
            // инициализация процесска начальными переменными в зависимости от типа
            switch (proc.KOD.ChildNodes[1].Name)
            {
                case "bubble": InitProcess.InitBuble(proc); break;
                case "Cloud": InitProcess.InitCloud(proc); break;
                case "Weed": InitProcess.InitWeed(proc); break;
                case "SmallFish": InitProcess.InitSmallFish(proc); break;
                case "Shark": InitProcess.InitShark(proc); break;
                case "VioletFish": InitProcess.InitVioletFish(proc); break;
                case "Skeleton": InitProcess.InitSkeleton(proc); break;
                case "TurquoiseFish": InitProcess.InitTurquoiseFish(proc); break;
                default: OsErrors.ThroError(OsErrors.Errors.SyntaxError); break;
            }
        }


        public void Update(List<Process> Processes, GameTime gameTime)
        {
            for (int i = 0; i < Processes.Count; i++)
            {
                XmlNodeList list = Processes[i].KOD.ChildNodes[1].ChildNodes;
                Evalute(list, Processes[i], gameTime); // выполнили команду
                #region Проверка ошибок
                if (Processes[i].state)
                {
                    // надо ведь проверить на ошибки?
                    if (Processes[i].position.X > OpSys.WorldWidth || Processes[i].position.X < 0 || Processes[i].position.Y < 0 || Processes[i].position.Y > OpSys.WorldHeight)
                        Processes[i].LastError = ApiErrors.EndOfRegion; // Процесс вышел за пределы региона                    
                    else
                        if (Processes[i].position.X > OpSys.WorldWidth || Processes[i].position.X < 0 || Processes[i].position.Y < OpSys.WaterBegin || Processes[i].position.Y > OpSys.DnoBegin )
                            Processes[i].LastError = ApiErrors.EndOfWater; // Процесс вышел за пределы воды

                    if (Processes[i].CheckCollision)
                    {
                        // коллизия с объектами
                        for (int j = 0; j < Processes.Count; j++)
                            if (i != j && Processes[j].CheckCollision)
                                if (Processes[i].position.Intersects(Processes[j].position))
                                {
                                    Processes[i].CollisionWith = Processes[j].ProcessType;
                                    Processes[i].LastError = ApiErrors.Collision; // Произошла коллизия
                                }
                    }
                }
                #endregion;
                else
                {
                    Processes.RemoveAt(i);
                }
            }
        }


        static void Evalute(XmlNodeList list, Process proc, GameTime gameTime)
        {
            for (int i = 0; i < list.Count; i++)
            {
                switch (list[i].Name)
                {
                    case "if":
                        {
                            string a = list[i].Attributes.Item(0).Value;
                            string b = list[i].Attributes.Item(1).Value;
                            a = ConvertVariableToValue(a, proc,gameTime);
                            b = ConvertVariableToValue(b, proc,gameTime);
                            if (a == b) // если условия в ифе совпадают
                            {
                                // вычислить иф
                                XmlNodeList list1 = list[i].ChildNodes;
                                Evalute(list1, proc,gameTime);
                            }
                            break;
                        }
                    case "ifAnd":
                            {
                                string a = list[i].Attributes.Item(0).Value;
                                a = ConvertVariableToValue(a, proc, gameTime);
                                bool f = true;
                                for (int p = 1; p < list[i].Attributes.Count; p++)
                                {
                                    string b = list[i].Attributes.Item(p).Value;
                                    b = ConvertVariableToValue(b, proc, gameTime);
                                    if (a != b)
                                    {
                                        f = false;
                                        break;
                                    }
                                }
                                if (f)
                                {
                                    XmlNodeList list1 = list[i].ChildNodes;
                                    Evalute(list1, proc, gameTime);
                                }
                                break;
                            }
                    case "ifOr":
                            {
                                string a = list[i].Attributes.Item(0).Value;
                                a = ConvertVariableToValue(a, proc, gameTime);
                                bool f = false;
                                for (int p = 1; p < list[i].Attributes.Count; p++)
                                {
                                    string b = list[i].Attributes.Item(p).Value;
                                    b = ConvertVariableToValue(b, proc, gameTime);
                                    if (a == b)
                                    {
                                        f = true;
                                        break;
                                    }
                                }
                                if (f)
                                {
                                    XmlNodeList list1 = list[i].ChildNodes;
                                    Evalute(list1, proc, gameTime);
                                }
                                break;
                            }
                    case "Move":
                        {
                            string a = list[i].Attributes.Item(0).Value; // x
                            string b = list[i].Attributes.Item(1).Value; // y
                            a = ConvertVariableToValue(a, proc,gameTime);
                            b = ConvertVariableToValue(b, proc,gameTime);
                            proc.position.X +=(int) GetValueOfFunction(a);
                            proc.position.Y += (int)GetValueOfFunction(b);
                            break;
                        }
                    case "DeleteMe":
                        {
                            proc.state = false;
                            break;
                        }
                    case "setq":
                        {
                            string a = list[i].Attributes.Item(0).Value; // куда записать значение
                            string b = list[i].Attributes.Item(1).Value; // что записать
                            b = ConvertVariableToValue(b, proc,gameTime);
                            double tmp = GetValueOfFunction(b); // вычисляем значение
                            switch (a)
                            {
                                case "A": proc.A = tmp; break;
                                case "B": proc.B = tmp; break;
                                case "C": proc.C = tmp; break;
                                case "D": proc.D = tmp; break;
                                case "Course": proc.course = tmp; break;
                                case "LastError": proc.LastError = ConvertIntToError((int)tmp); break;
                                case "CheckCollision":
                                    {
                                        if (tmp >= 0)
                                            proc.CheckCollision = true;
                                        else
                                            proc.CheckCollision = false;
                                        break;
                                    }
                            }
                            break;
                        }
                    case "invers":
                        {
                            string a = list[i].Attributes.Item(0).Value;
                            string b = list[i].Attributes.Item(0).Value;
                            b = ConvertVariableToValue(b, proc,gameTime);
                            double tmp = -GetValueOfFunction(b); // вычисляем значение
                            switch (a)
                            {
                                case "A": proc.A = tmp; break;
                                case "B": proc.B = tmp; break;
                                case "C": proc.C = tmp; break;
                                case "D": proc.D = tmp; break;
                                case "Course": proc.course = tmp; break;
                                case "LastError": proc.LastError = ConvertIntToError((int)tmp); break;
                                case "CheckCollision":
                                    {
                                        if (tmp >= 0)
                                            proc.CheckCollision = true;
                                        else
                                            proc.CheckCollision = false;
                                        break;
                                    }
                            }
                            break;
                        }
                }
            }
        }
       


        public void Draw(Process proc, SpriteBatch sprite, GameTime gameTime)
        {
            switch (proc.KOD.ChildNodes[1].Name)
            {
                case "bubble": DrawProcesses.DrawBubble(proc,sprite); break;
                case "Cloud": DrawProcesses.DrawCloud(proc, sprite); break;
                case "Weed": DrawProcesses.DrawWeed(proc, sprite); break;
                case "SmallFish": DrawProcesses.DrawSmallFish(proc, sprite); break;
                case "Shark":
                    {
                        DrawProcesses.AnimatForShar(proc, gameTime);
                        DrawProcesses.DrawShark(proc, sprite); break;
                    }
                case "VioletFish":
                    {
                        DrawProcesses.AnimatForVioletFish(proc, gameTime);
                        DrawProcesses.DrawVioletFish(proc, sprite); break;
                    }
                case "Skeleton": DrawProcesses.DrawSkeleton(proc, sprite); break;
                case "TurquoiseFish":
                    {
                        DrawProcesses.AnimatForTurquoiseFish(proc, gameTime);
                        DrawProcesses.DrawTurquoiseFish(proc, sprite); break;
                    }
                default: OsErrors.ThroError(OsErrors.Errors.SyntaxError); break;
            }
        }

        static double RadInGrad = 180 / Math.PI;
        static string ConvertVariableToValue(string str, Process proc, GameTime gameTime)
        {
            string result = str;
            if (proc.CheckCollision)
            {
                result = result.Replace("CheckCollision", "1");
            }
            else
            {
                result = result.Replace("CheckCollision", "-1");
            }
            result = result.Replace("CollisionWith", ((int)proc.CollisionWith).ToString());
            result = result.Replace("LastError", ((int)proc.LastError).ToString());
            result = result.Replace("None", "0");
            result = result.Replace("EndofRegion", "1");
            result = result.Replace("EndofWater", "2");
            result = result.Replace("Collision", "3");
            result = result.Replace("MustDie", "4");
            result = result.Replace("Course", proc.course.ToString());
            result = result.Replace("WorldWidth", OpSys.WorldWidth.ToString());
            result = result.Replace("WorldHeight", OpSys.WorldHeight.ToString());
            result = result.Replace("WaterBegin", OpSys.WaterBegin.ToString());
            result = result.Replace("WaterHeight", OpSys.WaterHeight.ToString());
            result = result.Replace("DnoBegin", OpSys.DnoBegin.ToString());
            result = result.Replace("DnoHeight", OpSys.DnoHeight.ToString());
            result = result.Replace("RadInGrad", RadInGrad.ToString());
            result = result.Replace("rand", OpSys.rand.Next().ToString());
            result = result.Replace("GetEllapsedTime", gameTime.ElapsedGameTime.Milliseconds.ToString());
            result = result.Replace("A", proc.A.ToString());
            result = result.Replace("B", proc.B.ToString());
            result = result.Replace("C", proc.C.ToString());
            result = result.Replace("D", proc.D.ToString());
            return result;
        }

        static double GetValueOfFunction(string function)
        {
            Parser pars = new Parser();
            double result;
            result = pars.Evaluate(function);
            return result;
        }

        static ApiErrors ConvertIntToError(int a)
        {
            ApiErrors result = ApiErrors.None;
            switch (a)
            {
                case 0: result = ApiErrors.None; break;
                case 1: result = ApiErrors.EndOfRegion; break;
                case 2: result = ApiErrors.EndOfWater; break;
                case 3: result = ApiErrors.Collision; break;
                case 4: result = ApiErrors.MustDie; break;
                default: OsErrors.ThroError(OsErrors.Errors.SyntaxError); break;
            }
            return result;
        }

        static ApiCommands ConvertIntToCommand(int a)
        {
            ApiCommands result = ApiCommands.None;
            switch(a)
            {
                case 0: result = ApiCommands.None; break;
                case 1: result = ApiCommands.Move; break;
                case 2: result = ApiCommands.DeleteMe; break;
                case 3: result = ApiCommands.CreateNew; break;
                case 4: result = ApiCommands.GiveMeListProcess; break;
                case 5: result = ApiCommands.DeleteNumber; break;
                default: OsErrors.ThroError(OsErrors.Errors.SyntaxError); break;
            }
            return result;
        }


    }
}

